package topo.sourcecode;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class LineNumber extends JComponent {
    static final long serialVersionUID = 3679449495322768618L;
    private static final Color DEFAULT_BACKGROUND = new Color(238, 238, 238);
    private static final Color DEFAULT_FOREGROUND = Color.GRAY;
    private static final Font DEFAULT_FONT = new Font("monospaced", 0, 12);
    private static final int HEIGHT = 2146483647;
    private static final int MARGIN = 5;
    private int lineHeight;
    private int fontLineHeight;
    private int currentRowWidth;
    private FontMetrics fontMetrics;

    public LineNumber(JComponent component) {
        if (component == null) {
            setBackground(DEFAULT_BACKGROUND);
            setForeground(DEFAULT_FOREGROUND);
            setFont(DEFAULT_FONT);
        } else {
            setBackground(DEFAULT_BACKGROUND);

            setForeground(DEFAULT_FOREGROUND);
            setFont(component.getFont());
        }
        setPreferredSize(9999);
    }

    public void setPreferredSize(int row) {
        int width = this.fontMetrics.stringWidth(String.valueOf(row));
        if (this.currentRowWidth < width) {
            this.currentRowWidth = width;
            setPreferredSize(new Dimension(10 + width, 2146483647));
        }
    }

    public void setFont(Font font) {
        super.setFont(font);
        this.fontMetrics = getFontMetrics(getFont());
        this.fontLineHeight = this.fontMetrics.getHeight();
    }

    public int getLineHeight() {
        if (this.lineHeight == 0) {
            return this.fontLineHeight;
        }
        return this.lineHeight;
    }

    public void setLineHeight(int lineHeight) {
        if (lineHeight > 0)
            this.lineHeight = lineHeight;
    }

    public int getStartOffset() {
        return 4;
    }

    public void paintComponent(Graphics g) {
        int lineHeight = getLineHeight();
        int startOffset = getStartOffset();
        Rectangle drawHere = g.getClipBounds();

        g.setColor(getBackground());
        g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);

        g.setColor(getForeground());
        int startLineNumber = drawHere.y / lineHeight + 1;
        int endLineNumber = startLineNumber + drawHere.height / lineHeight;
        int start = drawHere.y / lineHeight * lineHeight + lineHeight - startOffset;

        for (int i = startLineNumber; i <= endLineNumber; i++) {
            String lineNumber = String.valueOf(i);
            int width = this.fontMetrics.stringWidth(lineNumber);
            g.drawString(lineNumber, 5 + this.currentRowWidth - width, start);
            start += lineHeight;
        }
        setPreferredSize(endLineNumber);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("LineNumberDemo");
        frame.setDefaultCloseOperation(3);
        JPanel panel = new JPanel();
        frame.setContentPane(panel);
        panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        panel.setLayout(new BorderLayout());
        JTextArea textPane = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(textPane);
        panel.add(scrollPane);
        scrollPane.setPreferredSize(new Dimension(300, 250));
        LineNumber lineNumber = new LineNumber(textPane);
        lineNumber.setPreferredSize(99999);
        scrollPane.setRowHeaderView(lineNumber);
        frame.pack();
        frame.setVisible(true);
    }
}